//
//  WebViewWrapper.swift
//  study-swiftui
//
//  Created by liphone on 2021/6/3.
//

import SwiftUI
import WebKit;


final class WebViewWrapper: NSObject, UIViewControllerRepresentable,WKUIDelegate, WKNavigationDelegate{
    
    //定义存储属性
    var _webView:WKWebView?
    
    //定义交互属性
    var webView:WKWebView{
        get{
            _webView!
        }
        set{
            _webView = newValue
        }
    }
    
    /*
     一个绑定至外部的委托对象，用于分担职责
     */
    var webViewDelegate: WebViewDelegate?
    
    /*
     网页的快照图，如果有的话
     */
    var snapshot:UIImage?
    /*
     图标链接集合
     */
    public private(set) var faviconUrls = Set<ImageInfo>()
    
    /*
     网页图标
     */
    var favicon:UIImage?
    
    /*
     图片链接集合
     */
    var imageUrls = Set<ImageInfo>()
    
    var errorHappen:Bool = false;
    private var errorMsg:String?;
    
    var title:String?{
        webView.title
    }
    
    //UIOutlet
    let progressView = UIProgressView(progressViewStyle: .bar);
    
    func makeUIViewController(context: Context) ->  UIViewController {
        
        let viewController = UIViewController();
        
        let configuration = WKWebViewConfiguration()
        let userContentController = WKUserContentController()
        let handler = NotificationScriptMessageHandler(parent: self)
        //添加三个handler处理方法
        userContentController.add(handler, name: YRNKMessageType.submitAnImageInfo.rawValue)
        userContentController.add(handler,name:YRNKMessageType.JSCallIOS_Test.rawValue)
        userContentController.add(handler, name: YRNKMessageType.setImageAsset.rawValue)
        
        
        
        //注入脚本,对于懒加载的情况，js注入无效，无法取得有效资源
        //userContentController.addUserScript(userScript)
        
        //用户内容控制器
        configuration.userContentController = userContentController
        
        
        
        
        webView = WKWebView(frame: viewController.view.bounds, configuration: configuration)
        /*
         
         */
        
        YRNKLog(self.desc+" appNameForUA:",msg:configuration.applicationNameForUserAgent ?? "none")
        
        //设置webView 的委托对象
        webView.uiDelegate=self;
        webView.navigationDelegate=self;
        
        //如何使得progressView 的width撑满父级？
        progressView.frame.size.width=600
        
        
        //将进度条与webview添加进视图管理器的视图层次结构中
        viewController.view.addSubview(webView)
        viewController.view.addSubview(progressView)
        
        //设置进度的观察者，监控进度变化
        webView.addObserver(self, forKeyPath: "estimatedProgress", options:.new , context: nil);
        
        return viewController;
    }
    
    func updateUIViewController(_ viewController: UIViewController, context: Context) {
        //        let webView = uiViewController.view as! WKWebView
        //
        //
        //        if let _urlStr = urlStr {
        //            if let url = URL(string:_urlStr){
        //                let urlRequest = URLRequest(url:url)
        //                webView.load(urlRequest)
        //            }
        //        }
        //print("update ui view Controller")
        //YRNKLog(self.description, msg: "update ui view Controller")
    }
    
    /*
     进度值监控
     */
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if(keyPath == "estimatedProgress"){
            progressView.isHidden = webView.estimatedProgress ==  1
            progressView.setProgress(Float(webView.estimatedProgress), animated: true);
            print("web loading progress: \(webView.estimatedProgress )")
        }
        
    }
    
    
    
    func loadRequest(urlStr:String){
        
        if !urlStr.isEmpty{
            if let url = URL(string:urlStr){
                let urlRequest = URLRequest(url:url);
                print("jscall----------")
                errorHappen = false;
                webView.load(urlRequest)
                
            }
        }
        else{
            errorHappen = true
        }
        //
        
    }
    
    func goBack() -> Bool {
        errorHappen = false;
        if(webView.canGoBack){
            webView.goBack()
            return true
        } else {
            return false
        }
    }
    
    func goForward() -> Bool {
        errorHappen = false;
        if(webView.canGoForward){
            webView.goForward()
            return true
        } else {
            return false
        }
    }
    
    func setWebViewDelegate(delegate: WebViewDelegate) -> WebViewWrapper  {
        self.webViewDelegate = delegate
        return self
    }
    
    //WebView开始加载新页面时调用
    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        
        
        webViewDelegate?.urlChanged()
        webViewDelegate?.isLoading(loading: true)
        webViewDelegate?.onLoadStart(who: webView, navigation: navigation)
        
        
    }
    
    //WebView加载完成之后调用
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        progressView.setProgress(0, animated: false)
        progressView.isHidden=false;
        webViewDelegate?.isLoading(loading: false)
        
        
        if let title = webView.title, let urlString = webView.url?.absoluteString {
            //错误页\(^o^)/~h5 链接则不需要添加错误页进历史记录中
            if !errorHappen{
                webViewDelegate?.addHistory(title: title, url: urlString)
                
                let script1 = JSUtil.scriptForAddImageOnClickedEvent();
                webView.evaluateJavaScript(script1)
                
            }
        }
        //(title:  ?? "null", url: webView.url?.absoluteString ?? "null")
        takeSnapshot()
        self.updateFavicon()
        
        
        
        
        if errorHappen {
            let js = JSUtil.scriptForErrorPage(errorMsg: self.errorMsg ?? "未知错误")
            webView.evaluateJavaScript(js)
            errorHappen.toggle()
        }
        
        webViewDelegate?.onLoaded(who: webView, navigation: navigation)
        
    }
    
    //网页加载出现早期错误时调用
    func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
        YRNKLog("webView fails to load \(navigation.debugDescription)", msg: error.localizedDescription)
        //navigate to error page here
        //prepare url and js
        
        errorHappen =  true
        if let errorUrl = Bundle.main.url(forResource: "warningPage", withExtension: ".html", subdirectory: nil){
            
            //跳转到错误页
            
            webView.loadFileURL(errorUrl, allowingReadAccessTo: errorUrl)
            self.errorMsg = error.localizedDescription
            
            
        }
        
        
        
        webViewDelegate?.onFailed(who: webView,failWith: navigation, error: error)
    }
    
    //webView 将要显示上下文菜单前，可以做的预处理
    func webView(_ webView: WKWebView, contextMenuWillPresentForElement elementInfo: WKContextMenuElementInfo) {
        
    }
    
    //配置webView 对某些元素的上下文菜单
    func webView(_ webView: WKWebView, contextMenuConfigurationForElement elementInfo: WKContextMenuElementInfo, completionHandler: @escaping (UIContextMenuConfiguration?) -> Void) {
        
        
        //这里配置上下文菜单，唯一的难点就是不知道如何判断这个链接指向的内容                                                                                                                                                                                                                                                  
        if let linkUrl = elementInfo.linkURL {
            
            YRNKLog("context menu for url: ", msg: linkUrl.absoluteString)
            var configuration:UIContextMenuConfiguration
            
            if linkUrl.absoluteString.contains("isImage=true"){
                configuration = UIContextMenuConfiguration.init(identifier: nil, previewProvider: nil){
                    (elements)->UIMenu? in
                    
                    let actionOpenImageViewer = UIAction(title: "查看图片", image: UIImage(systemName:"photo"), identifier: nil, handler: {
                        action in
                        //这里拉起previewer
                        YRNKSharedThreadPool.dispatchTask(operation: {
                            let src = linkUrl.absoluteString
                            
                            if !self.imageUrls.isEmpty{
                                //find int master's set
                                
                                YRNKLog("长按图片src：", msg: src)
                                if let which = self.imageUrls.first(where: {
                                    YRNKLog("$0.url.absoluteString", item: $0.url.absoluteString)
                                    return src.contains($0.url.absoluteString)
                                }){
                                    //ElementOfResult 在compactMap中是否是会rethrow返回空？
                                    YRNKLog("点击图片的index", msg: which.index.description)
                                    self.webViewDelegate?.previewImage(index: which.index, imageUrls: self.imageUrls.compactMap({$0}).sorted(by: {$0.index < $1.index}))
                                }
                                else{
                                    if let img_info = ImageInfo.init(urlStr: src, width: nil, height: nil){
                                        img_info.index = 0
                                        self.webViewDelegate?.previewImage(index: img_info.index, imageUrls: [img_info])
                                    }
                                }
                            }
                            else{
                                
                                if let img_info = ImageInfo.init(urlStr: src, width: nil, height: nil){
                                    img_info.index = 0
                                    self.webViewDelegate?.previewImage(index: img_info.index, imageUrls: [img_info])
                                }
                            }
                        })
                    })
                    
                    let actionOpenLinkInThisTab = UIAction(title: "打开此链接", image: UIImage(systemName: "arrowshape.turn.up.right.fill"), identifier: nil, discoverabilityTitle: nil, handler: {
                        action in
                        self.loadRequest(urlStr: linkUrl.absoluteString)
                    })
                    
                    let actionOpenLinkInNewTab  = UIAction(title:"在新窗口打开此链接",image:UIImage(systemName: "square.and.arrow.up"), handler: {
                        action in
                        self.webViewDelegate?.openLinkInNewTab(link: linkUrl.absoluteURL)
                    })
                    
                    let actionCopyLink = UIAction(title:"复制此链接",image: UIImage(systemName:"doc.on.doc"),handler: {
                        action in
                        ClipBoardUtil.pasteStringOnClipBoard(str: linkUrl.absoluteString)
                    })
                    
                    return UIMenu.init(title: "关于此图片", image: UIImage(systemName: "link"), identifier: nil, options: .displayInline, children: [actionOpenImageViewer,actionOpenLinkInThisTab,actionOpenLinkInNewTab,actionCopyLink])
                }
            }
            else{
                configuration = UIContextMenuConfiguration.init(identifier: nil, previewProvider: nil){ (elements)->UIMenu? in
                    
                    let actionOpenLinkInThisTab = UIAction(title: "打开此链接", image: UIImage(systemName: "arrowshape.turn.up.right.fill"), identifier: nil, discoverabilityTitle: nil, handler: {
                        action in
                        self.loadRequest(urlStr: linkUrl.absoluteString)
                    })
                    
                    let actionOpenLinkInNewTab  = UIAction(title:"在新窗口打开此链接",image:UIImage(systemName: "square.and.arrow.up"), handler: {
                        action in
                        self.webViewDelegate?.openLinkInNewTab(link: linkUrl.absoluteURL)
                    })
                    
                    let actionCopyLink = UIAction(title:"复制此链接",image: UIImage(systemName:"doc.on.doc"),handler: {
                        action in
                        ClipBoardUtil.pasteStringOnClipBoard(str: linkUrl.absoluteString)
                    })
                    
                    
                    return UIMenu.init(title: "关于此链接，你想", image: UIImage(systemName: "link"), identifier: nil, options: .displayInline, children: [actionOpenLinkInThisTab,actionOpenLinkInNewTab,actionCopyLink])
                }
            }
            completionHandler(configuration)
        }
        else{
            completionHandler(nil)
        }
        
    }
    
    
    
    //请求拦截
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) {
        
        //self.onNavigate(webView,navigationAction,preferences,decisionHandler);
        //通过navigationAction.request 获取请求url信息
        // 通过preference 设置偏好信息 preferences.preferredContentMode = .desktop
        //是否允许js 执行 preferences.allowsContentJavaScript
        decisionHandler(.allow,preferences)
    }
    
    
    let desc = "WebViewWrapper"
    
    
    //设置浏览器UA
    func setUserAgent(UA:UserAgent){
        self.webView.customUserAgent = UA.rawValue
    }
    
    
    
    //异步获取快照
    func takeSnapshot(){
        let snapshotConfiguration = WKSnapshotConfiguration.init()
        snapshotConfiguration.afterScreenUpdates = true
        webView.takeSnapshot(with: snapshotConfiguration, completionHandler: {
            (image:UIImage?,error:Error?)->Void in
            if image != nil {
                self.snapshot = image
            }
            else{
                //这里可以配置错误页快照
                self.snapshot = UIImage.init(named: "zcprovided-png")
            }
        })
        
    }
    
    private func parseFavicon(){
        if let faviconUrl = faviconUrls.first{
            if let iconData = try? Data.init(contentsOf: faviconUrl.url){
                self.favicon = UIImage.init(data: iconData)
                //通知webViewDelegate, 发生变更
                //设置favicon
                //主线程同步
                YRNKSharedThreadPool.dispatchUITask {
                    self.webViewDelegate?.onFaviconChanged(favicon: self.favicon);
                }
            }
        }
    }
    
    
    private func updateFavicon() {
        self.webView.evaluateJavaScript(JSUtil.GET_FAVICON_SCRIPT) { (result, error) in
            // Favicon is returned from Javascript as an NSMutableArray with NSMutableDictionaries
            // Needs to be turned into a list
            var faviconUrlsSet = Set<ImageInfo>();
            
            
            //从result中返回
            if let favicons = result as? NSArray {
                for faviconObject in favicons {
                    //Array每一个元素，都是NSDictionary类型，需要取出url加载图片
                    if let faviconDictionary = faviconObject as? NSDictionary {
                        
                        if let urlString = faviconDictionary["url"] as? String {
                            
                            
                            //相对于webView当前url，执行url拼接。
                            if let url = URL(string: urlString, relativeTo: self.webView.url) {
                                let sizesString = faviconDictionary["sizes"] as? String;
                                //切分字符串获取高宽属性，可能为空
                                let sizeStrings = sizesString?.components(separatedBy: "x") ?? []
                                
                                if (sizeStrings.count == 2) {
                                    let width = Int(sizeStrings[0])
                                    let height = Int(sizeStrings[1])
                                    faviconUrlsSet.insert(ImageInfo(url: url, width: width, height: height))
                                } else {
                                    faviconUrlsSet.insert(ImageInfo(url: url, width: nil, height: nil))
                                }
                            }
                        }
                    }
                }
            }
            
            //可能找不到favicon，此时需要从 $baseUrl$/favicon.ico中取
            if faviconUrlsSet.count == 0 {
                if let webViewUrl = self.webView.url {
                    //只需要取得baseUrl,删掉url后的扩展参数
                    let schemeAndHostUrl = webViewUrl.deletingPathExtension()
                    let url = schemeAndHostUrl.appendingPathComponent("favicon.ico")
                    faviconUrlsSet.insert(ImageInfo(url: url, width: nil, height: nil))
                }
            }
            
            //赋值临时变量
            if (faviconUrlsSet != self.faviconUrls) {
                self.faviconUrls = faviconUrlsSet
                YRNKSharedThreadPool.dispatchTask(operation: {
                    self.parseFavicon()
                })
            }
            
            //debug
            if let first = self.faviconUrls.first{
                YRNKLog("webview: fetch favicon'url", msg: first.url.absoluteString)
            }
            
            if error != nil {
                YRNKLog("webview: 执行图标获取js出错", msg: error?.localizedDescription ?? "")
            }
        }
    }
    
    //FaviconUrl.swift
    
    func getHTMLTitle()->Void{
        webView.evaluateJavaScript("console.log(\"aaaa\")", completionHandler: nil)
        
    }
    
    
    
    /// jsBridge方法回调对象：
    class NotificationScriptMessageHandler: NSObject, WKScriptMessageHandler {
        
        /*
         外部弱引用
         */
        unowned let master:WebViewWrapper
        
        init(parent:WebViewWrapper) {
            master = parent
        }
        
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            switch message.name {
            case YRNKMessageType.submitAnImageInfo.rawValue:
                /*
                 这里拿到其中一个图片，之后通过匹配url来看选中哪一个图片
                 */
                
//                if let payload = message.body as? String{
//                    YRNKSharedThreadPool.dispatchTask(operation: {
//
//                        let img_info_json = JSON.init(parseJSON: payload)
//                        if let src = img_info_json["src"].string {
//                            //find in master's set
//                            //到了这里，master.imageUrls中如果为0，可能需要在这里手动构建一个数组
//                            if !self.master.imageUrls.isEmpty{
//                                //find int master's set
//                                YRNKLog("长按图片src：", msg: src)
//                                if let which = self.master.imageUrls.first(where: {
//                                    $0.url.absoluteString == src
//                                }){
//                                    //ElementOfResult 在compactMap中是否是会rethrow返回空？
//                                    YRNKLog("点击图片的index", msg: which.index.description)
//                                    self.master.webViewDelegate?.previewImage(index: which.index, imageUrls: self.master.imageUrls.compactMap({$0}).sorted(by: {$0.index < $1.index}))
//                                }
//                            }
//                            else{
//                                let w = img_info_json["width"].int, h = img_info_json["height"].int
//                                if let img_info = ImageInfo.init(urlStr: src, width: w, height: h){
//                                    img_info.index = 0
//                                    self.master.webViewDelegate?.previewImage(index: img_info.index, imageUrls: [img_info])
//                                }
//                            }
//                        }
//
//
//                    })
//                }
                
                
                
                
                //检查是否已经解析了图片，如果未解析则先解析，再唤起其他页面
                
                
                YRNKLog(self.desc, msg: "用户点击了一个不跳转的图片")
            case YRNKMessageType.JSCallIOS_Test.rawValue:
                //JSBridge测试代码
                YRNKLog(self.desc, item: message.body)
            case YRNKMessageType.setImageAsset.rawValue:
                YRNKLog(self.desc, msg:" jscall setImageAsset")
                //这里拿到一个Array
                if let payload = message.body as? String{
                    let imgAssetInfo = JSON.init(parseJSON: payload)
                    
                    //生成imageUrls 集合
                    if let set = self.castImageInfoFromJSONToSet(asset: imgAssetInfo){
                        master.imageUrls = set
                        for i in master.imageUrls{
                            YRNKLog("图片元素",msg: i.url.absoluteString)
                        }
                    }
                    
                    YRNKLog(self.desc, item: imgAssetInfo)
                    //YRNKLog(self.desc, msg:payload)
                }
                
                
            default:
                break
            }
        }
        
        let desc = "JSBridge-MessageHandler"
        
        
        //将json格式的图片信息转换成Set<>
        func castImageInfoFromJSONToSet(asset:JSON)->Set<ImageInfo>?{
            if let array = asset.array{
                var set = Set<ImageInfo>.init()
                
                var i = 0;
                for el in array{
                    if let src = el["src"].string{
                        if let imgInfo = ImageInfo.init(urlStr: src, width: el["width"].int, height: el["height"].int){
                            imgInfo.index  = i;
                            i += 1;
                            set.insert(imgInfo);
                        }
                    }
                }
                if set.isEmpty {
                    return nil
                }
                return set
            }
            return nil
        }
    }
    
    
}


